<?php

namespace App\Trip\Translate\Niutrans;


use Carbon\Carbon;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Cache;

/**
 * 注意事项
 *
 * 1.翻译文本（src_text字段）必须为UTF-8编码。
 *
 * 2.在发送HTTP请求之前需要对src_text字段参数进行URL Encode。
 *
 * 3.如果请求字符串长度大于1500，请使用POST方式提交请求。
 *
 * 4.Content-Type ：仅支持【application/json】、【application/x-www-form-urlencoded】。
 *
 * 5.dictNo 与 memoryNo 为空时，则使用控制台中用户设置的默认库。
 */
class Translation
{
    /**
     * 支持请求长度
     */
    const MAX_TEXT_LENGTH = 5000;
    const ERROR_CODES
                          = [
            '-1'     => '未开始翻译',
            '10000'  => '输入为空',
            '10001'  => '请求频繁，超出QPS限制',
            '10003'  => '请求字符串长度超过限制',
            '10005'  => '源语编码有问题，非UTF-8',
            '13001'  => '字符流量不足或者没有访问权限',
            '13002'  => '"apikey"参数不可以是空',
            '13003'  => '内容过滤异常',
            '13007'  => '语言不支持',
            '13008'  => '请求处理超时',
            '14001'  => '分句异常',
            '14002'  => '分词异常',
            '14003'  => '后处理异常',
            '14004'  => '对齐失败，不能够返回正确的对应关系',
            '000000' => '请求参数有误，请检查参数',
            '000001' => 'Content-Type不支持【multipart/form-data】',
        ];
    // 支持的语种
    const SUPPORT_LANGUAGES
        = [
            'zh',
            'uy',
        ];
    /**
     * API密钥
     * @var string
     */
    private string $api_key;
    private string $url;
    /**
     * 源语言：待翻译文本语种参数
     * 默认中文
     */
    private string $from = 'zh';
    /**
     * 目标语言：翻译目标语种参数
     * 默认维吾尔语
     * @var string
     */
    private string $to = 'uy';
    /***
     * 待翻译字符串
     * @var string
     */
    private string $src_text = '';
    /**
     * 翻译结果
     * @var string
     */
    private string $translation = '';
    /**
     * @var int
     */
    private int $code = -1;
    /**
     * @var string
     */
    private string $message = '未开始翻译';
    private Client $client;
    private array  $chunks  = [];
    private string $baseCacheKey;


    /**
     * @throws Exception
     */
    public function __construct(string $src_text, $from = 'zh', $to = 'uy')
    {
        $this->api_key = config('translator.niutrans.api_key');
        $this->url     = config('translator.niutrans.url');
        $this->setText($src_text);
        $from && $this->setFrom($from);
        $to && $this->setTo($to);
        $this->client       = new Client();
        $this->baseCacheKey = "Trans:Niutrans";
    }

    private function setText(string $text): void
    {
        $this->src_text = $this->trim($text);
    }

    private function trim(string $text): string
    {
        return trim($text);
    }

    /**
     * @throws Exception
     */
    public function setFrom($from): static
    {
        if (!$from) {
            throw new Exception('from error');
        }

        if (!in_array($from, self::SUPPORT_LANGUAGES)) {
            throw new Exception('from language not support');
        }

        $this->from = $from;
        return $this;
    }

    /**
     * @throws Exception
     */
    public function setTo($to): static
    {
        if (!$to) {
            throw new Exception('to error');
        }

        if (!in_array($to, self::SUPPORT_LANGUAGES)) {
            throw new Exception('to language not support');
        }
        $this->to = $to;
        return $this;
    }

    /**
     * @throws GuzzleException
     * @throws Exception
     */
    public function translate(): static
    {
        // $frequency cache
        $now   = Carbon::now()->timestamp;
        $fq_ck = "{$this->baseCacheKey}:QPS:{$now}";
        if (!Cache::has($fq_ck)) {
            Cache::put($fq_ck, 50, 1);
        }
        $src_text = $this->src_text;
        if ($this->to == $this->from) {
            $this->translation = $src_text;
            $this->code        = 0;
            $this->message     = '无需翻译';
            return $this;
        }
        if (empty($src_text)) {
            $this->translation = '';
            $this->code        = 10000;
            $this->message     = self::ERROR_CODES[$this->code] ?? $this->message;
            return $this;
        }
        // cache_key
        $cache_key = "$this->baseCacheKey:{$this->from}:{$this->to}".md5($src_text);
        if (Cache::has($cache_key)) {
            $this->translation = Cache::get($cache_key);
        } else {
            // url_encode
            $this->chunkText($src_text);
            $translations = [];

            foreach ($this->chunks as $key => $str) {
                $encode         = $str;//urlencode($str);
                $part_cache_key = "$this->baseCacheKey:{$this->from}:{$this->to}:".md5($str);
                if (!Cache::has($part_cache_key)) {
                    $jsonData = [
                        "from"     => $this->from,
                        "to"       => $this->to,
                        "apikey"   => $this->api_key,
                        "src_text" => $encode,
                    ];
                    $re       = Cache::decrement($fq_ck);
                    if ($re <= 0) {
                        sleep(1);
                    }
                    $res = $this->client->post($this->url, [
                        "json" => $jsonData,
                    ]);

                    $contents = $res->getBody()->getContents();
                    $data     = json_decode($contents, true);
                    if (isset($data['error_code'])) {
                        $this->code    = $data['error_code'];
                        $this->message = $data['error_msg'];
                        return $this;
                    }

                    $result             = $data['tgt_text'];
                    $translations[$key] = $result;
                    Cache::put($part_cache_key, $result, 10 * config('trip.ttl.day'));
                } else {
                    $translations[$key] = Cache::get($part_cache_key);
                }
            }
            // 这里可以按照 key 排序
            $this->translation = implode('', $translations);
            Cache::put($cache_key, $this->getTranslation(), 30 * config('trip.ttl.day'));
        }

        $this->code    = 0;
        $this->message = 'success';
        return $this;
    }

    private function chunkText(string $text): void
    {
        $text = $this->trim($text);
        $len  = mb_strlen($text);
        if ($len == 0) {
            return;
        }

        if ($len <= self::MAX_TEXT_LENGTH) {
            $this->chunks[] = $text;
            return;
        }
        $remain         = $len - self::MAX_TEXT_LENGTH;
        $chunk_count    = count($this->chunks);
        $start          = $chunk_count * self::MAX_TEXT_LENGTH;
        $txt            = mb_substr($text, $start, self::MAX_TEXT_LENGTH);
        $this->chunks[] = $txt;
        if ($remain > 0) {
            $rxt = mb_substr($text, -$remain);
            $this->chunkText($rxt);
        }
    }

    /**
     * 获取翻译结果
     * @return string
     */
    public function getTranslation(): string
    {
        return $this->translation;
    }

    public function getErrCode(): int
    {
        return $this->code;
    }

    public function getMessage(): string
    {
        return $this->message;
    }

    private function getChunks(): array
    {
        return array_filter($this->chunks);
    }

    private function chunkTranslate($src_text)
    {

    }
}